package io.xxx.omni.osc.sync

import com.alibaba.fastjson.JSONObject
import io.xxx.omni.osc.common.toJSONObject
import io.xxx.omni.osc.common.toLocalDateTime
import io.xxx.omni.osc.common.zoneOffset
import io.xxx.omni.osc.domain.*
import io.xxx.omni.osc.sdk.TxhjClient
import org.springframework.stereotype.Component
import org.springframework.util.StringUtils
import java.security.MessageDigest
import java.time.Duration
import java.time.LocalDateTime
import java.util.*
import javax.crypto.Cipher
import javax.crypto.spec.IvParameterSpec
import javax.crypto.spec.SecretKeySpec

/**
 * 腾讯惠聚
 */
@Suppress("UnstableApiUsage")
abstract class TxhjPorter : GenericPageDocumentPorter(), PlatformIdentity.TengXunHuiJu {

    protected abstract val path: String

    override val permitsPerMinutes = 180

    override val pageSize = 50

    override val duration: Duration = Duration.ofMinutes(30)

    override fun getStartTime(store: Store, jobCheckpoint: JobCheckpoint): LocalDateTime {
        val minStartTime = LocalDateTime.now().minusMonths(3).plusMinutes(1)
        val startTime = jobCheckpoint.endTime
        return if (startTime < minStartTime) minStartTime else startTime
    }

    override fun getResponse(context: JobContext, pageNo: Int, hasTotal: Boolean): JSONObject {
        val (store, startTime, endTime) = context
        val request = buildRequest(startTime, endTime, pageNo)
        val client = applicationContext.getBean(TxhjClient::class.java)
        val response = client.execute(store, path, request)
        val code = response.getInteger("code")
        if (code != 0) {
            throwException(context, request, response)
        }
        return response.getJSONObject("data")
    }

    override fun getCount(context: JobContext, response: JSONObject): Int = response.getInteger("totalCount")

    protected abstract fun buildRequest(
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        pageNo: Int,
    ): Map<String, Any>
}

// 订单
@Component
class TxhjTradePorter : TxhjPorter() {

    override val documentType = DocumentType.TRADE

    override val path = "/order/list/increment"

    override fun buildDocuments(context: JobContext, response: JSONObject): List<Document> {
        val dataKey = context.store.authorization["dataKey"]
        return response
            .getJSONArray("orderList")
            .map {
                val value = it.toJSONObject()
                value["userName"] = decrypt(value.getString("userName"), dataKey)
                value["telNumber"] = decrypt(value.getString("telNumber"), dataKey)
                value["provinceName"] = decrypt(value.getString("provinceName"), dataKey)
                value["cityName"] = decrypt(value.getString("cityName"), dataKey)
                value["countyName"] = decrypt(value.getString("countyName"), dataKey)
                value["detailInfo"] = decrypt(value.getString("detailInfo"), dataKey)
                value["deliverNumber"] = decrypt(value.getString("deliverNumber"), dataKey)
                value["deliverCompany"] = decrypt(value.getString("deliverCompany"), dataKey)
                val sn = value.getString("orderId")
                val status = value.getString("orderStatus")
                val modified = value.getLong("updateTime").toLocalDateTime()
                Document(sn, status, modified, value)
            }
    }

    override fun buildRequest(
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        pageNo: Int
    ): Map<String, Any> {
        return mapOf(
            "startUpdateTime" to startTime.toEpochSecond(zoneOffset),
            "endUpdateTime" to endTime.toEpochSecond(zoneOffset),
            "pageNum" to pageNo,
            "pageSize" to pageSize,
        )
    }

    private fun decrypt(encryptedData: String, dataKey: String): String? {
        return if (!StringUtils.hasText(encryptedData)) {
            null
        } else decrypt(Base64.getDecoder().decode(encryptedData), dataKey)
    }

    private fun decrypt(encryptedIvTextBytes: ByteArray, key: String): String {
        val ivSize = 16
        val keySize = 16
        val iv = ByteArray(ivSize)
        System.arraycopy(encryptedIvTextBytes, 0, iv, 0, iv.size)
        val ivParameterSpec = IvParameterSpec(iv)
        val encryptedSize = encryptedIvTextBytes.size - ivSize
        val encryptedBytes = ByteArray(encryptedSize)
        System.arraycopy(encryptedIvTextBytes, ivSize, encryptedBytes, 0, encryptedSize)
        val keyBytes = ByteArray(keySize)
        val md = MessageDigest.getInstance("SHA-256")
        md.update(key.toByteArray())
        System.arraycopy(md.digest(), 0, keyBytes, 0, keyBytes.size)
        val secretKeySpec = SecretKeySpec(keyBytes, "AES")
        val cipherDecrypt = Cipher.getInstance("AES/CBC/PKCS5Padding")
        cipherDecrypt.init(2, secretKeySpec, ivParameterSpec)
        val decrypted = cipherDecrypt.doFinal(encryptedBytes)
        return String(decrypted)
    }
}

@Component
class TxhjRefundPorter : TxhjPorter() {

    override val documentType = DocumentType.REFUND

    override val path = "/aftersale/list/increment"

    override fun buildDocuments(context: JobContext, response: JSONObject): List<Document> {
        return response
            .getJSONArray("list")
            .map {
                val value = it.toJSONObject()
                val sn = value.getString("afterSaleOrderId")
                val rsn = value.getString("orderId")
                val status = value.getString("status")
                val modified = value.getLong("updateTime").toLocalDateTime()
                Document(sn, rsn, status, modified, value)
            }
    }

    override fun buildRequest(
        startTime: LocalDateTime,
        endTime: LocalDateTime,
        pageNo: Int
    ): Map<String, Any> {
        return mapOf(
            "startUpdateTime" to startTime.toEpochSecond(zoneOffset),
            "endUpdateTime" to endTime.toEpochSecond(zoneOffset),
            "pageNum" to pageNo,
            "pageSize" to pageSize,
        )
    }
}